package com.tre.jdevtemplateboot.aop;

import com.tre.jdevtemplateboot.annotatiaon.RequestLimiterValidate;
import com.tre.jdevtemplateboot.common.jwt.IDataBaseRequestLimiterValidationService;
import com.tre.jdevtemplateboot.common.util.HttpUtils;
import com.tre.jdevtemplateboot.common.util.IPUtils;
import com.tre.jdevtemplateboot.domain.po.RequestLimiter;
import com.tre.jdevtemplateboot.exception.business.SysFrequentRequestsException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author JDev
 */
@Aspect
@Component
@Order(3)
public class RequestLimiterValidationAspect {

    private static final String LIMITER_KEY = "limiter:%s:%s";

    @Autowired(required=false)
    private IDataBaseRequestLimiterValidationService iDataBaseRequestLimiterValidationService;

    @Pointcut("@annotation(requestLimiterValidate)")
    public void pointcut(RequestLimiterValidate requestLimiterValidate) {
        throw new UnsupportedOperationException();
    }

    @Around("pointcut(requestLimiterValidate)")
    public Object around(ProceedingJoinPoint pjp, RequestLimiterValidate requestLimiterValidate) throws Throwable {
        String ipAddress = IPUtils.getIPAddress(HttpUtils.getHttpServletRequest());
        String methodName = pjp.getSignature().toLongString();

        int limiterType = requestLimiterValidate.limiterType();
        int cycle = requestLimiterValidate.cycle();
        int frequency = requestLimiterValidate.frequency();
        long currentTime = System.currentTimeMillis();

        String limiterKey = String.format(LIMITER_KEY, ipAddress, methodName);
        RequestLimiter requestLimiter = iDataBaseRequestLimiterValidationService
                .oneRequestLimiter(limiterType,limiterKey);

        long beginTime = 0;
        Integer exFrequency = 0;
        if (requestLimiter != null) {
            beginTime = requestLimiter.getLimiterBeginTime();
            exFrequency = requestLimiter.getLimiterFrequency();
        }

        if (currentTime - beginTime > cycle) {
            updateRequestLimiter(limiterType, limiterKey, currentTime, 1);
        } else {
            if (exFrequency < frequency) {
                updateRequestLimiter(limiterType, limiterKey, currentTime, exFrequency + 1);
            } else {
                throw new SysFrequentRequestsException(requestLimiterValidate.message());
            }
        }
        return pjp.proceed();
    }

    private void updateRequestLimiter(int limiterType, String limiterKey, long currentTime, int exFrequency) {
        RequestLimiter saveLimiter = new RequestLimiter();
        saveLimiter.setLimiterType(limiterType);
        saveLimiter.setLimiterKey(limiterKey);
        saveLimiter.setLimiterBeginTime(currentTime);
        saveLimiter.setLimiterFrequency(exFrequency);
        iDataBaseRequestLimiterValidationService.updateRequestLimiter(saveLimiter);
    }
}
